#ifndef __GLF_CORE_THREAD_H_INCLUDED__
#define __GLF_CORE_THREAD_H_INCLUDED__

#include <glf/config.h>

#include <cstddef>

#include <glf/core/tls.h>

#undef Yield
#undef Status

namespace glf {

struct Runnable : private NonCopyable {
	virtual ~Runnable() {}
	bool CanRun();
	virtual void Run() = 0;
};

struct ThreadMgr;

struct IsMirrorThread
{
};

const IsMirrorThread isMirrorThread = {};

#if GLF_AS_DLL
// Our TLs is set as a struct in provision for future additions
struct NativeTls
{
	long mId;
};
GLF_DECLARE_TLS(NativeTls, mNativeTls, static);

extern const int THREAD_PRIORITY_MIN;	//min <= norm <= max
extern const int THREAD_PRIORITY_NORM;
extern const int THREAD_PRIORITY_MAX;

int GetThreadPriorityMin();
int GetThreadPriorityMax();
int GetThreadPriorityNorm();

class Thread : public Runnable {
private:

#else

class Thread : public Runnable {
private:
	// Our TLs is set as a struct in provision for future additions
	struct NativeTls
	{
		long mId;
	};

	GLF_DECLARE_TLS(NativeTls, mNativeTls, static);

#endif
	static unsigned int usedThreadId;

public:
	enum Status {
		STATUS_WAITINGTOSTART,
		STATUS_RUNNING,
		STATUS_SUSPENDED,
		STATUS_STOPPED
	};
	enum Features {
		FEATURE_NONE				= 0x0000,
		FEATURE_MAX_THREADS			= 0x000f,
		FEATURE_MAIN_CAN_SLEEP		= 0x0010,
		FEATURE_CAN_SLEEP			= 0x0020,
		FEATURE_CAN_SUSPEND_RESUME	= 0x0040,
		FEATURE_NEEDRUNNABLECANRUN	= 0x0080
	};
	enum SchedulingPolicy {
		SCHEDULINGPOLICY_START = 0,
		SCHEDULINGPOLICY_FIFO = SCHEDULINGPOLICY_START,
		SCHEDULINGPOLICY_ROUNDROBIN,
		SCHEDULINGPOLICY_BATCH,
		SCHEDULINGPOLICY_IDLE,
		SCHEDULINGPOLICY_OTHER,
		SCHEDULINGPOLICY_UNKNOWN,
		SCHEDULINGPOLICY_COUNT,
	};

	bool IsSchedulingSupported(SchedulingPolicy policy) const;

	static const char* GetSchedulingPolicyName(SchedulingPolicy mode);

	virtual ~Thread();

	int GetPriority() const;
	void RaisePriority(size_t step=1);
	void LowerPriority(size_t step=1);
	void SetPriority(int npriority);

#if GLF_AS_DLL

	void Start(int priority = GetThreadPriorityNorm());
	void Start(Runnable& runnable, int priority = GetThreadPriorityNorm());

#else
	static const int PRIORITY_MIN;	//min <= norm <= max
	static const int PRIORITY_NORM;
	static const int PRIORITY_MAX;

	void Start(int priority = PRIORITY_NORM);
	void Start(Runnable& runnable, int priority = PRIORITY_NORM);

#endif

	static int GetPriorityMax();
	static int GetPriorityMin();
	static int GetPriorityNorm();

	SchedulingPolicy GetSchedulingPolicy();
	void SetSchedulingPolicy(SchedulingPolicy mode);

	void SetCoreAffinity(int flag);
	int GetCoreAffinity() const;

	void Join();

	void Suspend();
	void Resume();

	Status GetStatus() const;

	virtual void Run();

	//! Check is this thread is the thread0
	bool IsMain() const;

	//!
	bool IsCurrent() const;

	void SetName(const stringc& name);
	const stringc GetName() const;

	//! Check is the current thread is the main thread: ie. the thread0 or 
	//! another thread setted by App::SetAsMainThread()
	//! Warning: with this->IsCurrent() == true, this->IsMain() is not necessary
	//! equals to Thread::sIsMain()...
	static bool sIsMain();

	static Features GetFeatures();

	static void GetNumberOfThreads(int& threads, int& glfThreads);

	static int GetSequentialThreadId();
	static void ReleaseSequentialThreadId();
	static Thread& GetCurrent();
	static void Sleep(uint ms);
	static void Yield();

	// Those two functions are exposed only to allow applications with threads
	// not launched via GLF to call registered initilizers/destroyers (see
	// PushStartExitHandlers/PopStartExitHandlers)
	static void OnStart();
	static void OnExit();

	static int AddStartExitHandlers(void (*onStart)(void*), void (*onExit)(void*), void* arg);
	static void* RemoveStartExitHandlers(int id); // returns arg from correspond AddStartExitHandlers which returned id

protected:
	Thread();	 //main thread ctor
	Thread(IsMirrorThread); //mirror thread ctor
	Thread(uint stackSize);

private:
	friend struct ThreadMgr;
	friend void Sleep(uint ms);

	static void MyRun(Thread& thread);

	void Init();

	void _Sleep(uint ms);
	void _Yield();


	Runnable* mRunnable;

	uint mStackSize;
	volatile Status mStatus;	// volitle: mStatus is accessed and modified in its maker thread, and in its own thread.
	int mPriority;
	int mIsMirrorThread;
	stringc mName;

	GLF_DECLARE_IMPL(1*1024)
};

template<uint SIZE>
class ThreadT : public Thread {
public:
	ThreadT() :
		Thread(SIZE) {
	}
private:
};

class Thread4k : public ThreadT<4*1024> {
public:
	Thread4k() :
		ThreadT<4*1024>() {
	}
};

class Thread16k : public ThreadT<16*1024> {
public:
	Thread16k() :
		ThreadT<16*1024>() {
	}
};

inline int GetCurrentSequentialThreadId() {
	return Thread::GetSequentialThreadId();
}

} //namespace glf

GLF_ENUM_FLAG(glf::Thread::Features)

#endif //__GLF_CORE_THREAD_H_INCLUDED__

